﻿using System.Collections.Generic;
using System.Data;
using System.Linq;
using DataScripter.Util;

namespace DataScripter.Scripting.Compare.DataTables.Data
{
    public class DataTableDataComparer
    {
        private readonly string[] _columnNames;
        private DataTable Table1 { get; set; }
        private DataTable Table2 { get; set; }
        private string Sort { get; set; }

        public DataTableDataComparer(DataTable table1, DataTable table2)
        {
            Guard.TablesShouldHaveSameStructure(table1, table2);
            Guard.TableShouldHavePrimaryKey(table1);

            Table1 = table1;
            Table2 = table2;
            _columnNames = Table1.PrimaryKey.Select(c => c.ColumnName).ToArray();
            Sort = string.Join(",", _columnNames);
        }

        public DataTableDataComparisonResult Compare()
        {
            DataTable table1 = new DataView(Table1) { Sort = Sort }.ToTable();
            DataTable table2 = new DataView(Table2) { Sort = Sort }.ToTable();
            DataTableDataComparisonResult result = new DataTableDataComparisonResult();

            ObjectsComparer comparer = new ObjectsComparer();

            int count1 = table1.Rows.Count;
            int count2 = table2.Rows.Count;
            int current1 = 0;
            int current2 = 0;
            bool table2Ended = current2 == count2;
            List<int> ordinals = _columnNames.Select(c => table1.Columns[c].Ordinal).ToList();
            for (; current1 < count1; current1++)
            {
                DataRow currentRow1 = table1.Rows[current1];
                
                if (table2Ended)
                {
                    result.RowsToInsert.Add(currentRow1);
                    continue;
                }                

                DataRow currentRow2 = table2.Rows[current2];
                object[] keyValues1 = ordinals.Select(o => currentRow1[o]).ToArray();
                object[] keyValues2 = ordinals.Select(o => currentRow2[o]).ToArray();

                int keyComparison = comparer.Compare(keyValues1, keyValues2);
                if (keyComparison < 0)
                {
                    result.RowsToInsert.Add(currentRow1);
                    continue;
                }
                if (keyComparison == 0)
                {
                    // possible update
                    int allFieldComparison = comparer.Compare(currentRow1.ItemArray, currentRow2.ItemArray);

                    if (allFieldComparison != 0)
                    {
                        result.RowsToUpdate.Add(currentRow2, currentRow1);
                    }
                    table2Ended = ++current2 == count2;
                    continue;
                }
                while (!table2Ended && keyComparison > 0)
                {
                    result.RowsToDelete.Add(currentRow2);

                    table2Ended = ++current2 == count2;
                    if (table2Ended) continue;

                    currentRow2 = table2.Rows[current2];
                    keyValues2 = ordinals.Select(o => currentRow2[o]).ToArray();
                    keyComparison = comparer.Compare(keyValues1, keyValues2);

                    if (keyComparison == 0)
                    {
                        table2Ended = ++current2 == count2;
                    }
                    else if(keyComparison < 0)
                    {
                        result.RowsToInsert.Add(currentRow1);
                        break;
                    }
                }
            }

            while (!table2Ended)
            {
                DataRow currentRow2 = table2.Rows[current2];
                result.RowsToDelete.Add(currentRow2);

                table2Ended = ++current2 == count2;
            }
            return result;
        }
    }
}
